﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Text;
using System.Drawing;
using SdmxMl.Excel;
using SdmxMl.Common;
using SdmxMl.GenericMetadata;
using SdmxMl.Manager;
using SdmxMl.Structure;
using Telerik.WinControls;
using Telerik.WinControls.Data;
using Telerik.WinControls.UI;
using SasPlugIn.DB;


namespace SasPlugIn.Table
{
    class GridManager
    {
        #region Constants

        private const string TableMain = "Main";
        private const string TableRefCodelist = "RefCodelist";
        private const string ColGuid = "GUID";
        private const string ColPrefix = "Prefix";
        private const string ColTableName = "Name";
        private const string ColType = "Type";
        private const string ColFullId = "Full Id";
        private const string ColMode = "Mode";
        private const string ColStarParam = "Param";
        private const string CodelistRelation = "relCodelist";
        private const string TYPE_DSD = "DSD";
        private const string TYPE_CODELIST = "CodeLists";
        private const string TYPE_CONCEPTSCHEME = "Concepts";

        #endregion

        #region Internal Members

        /// <summary>The grid control to manage</summary>
        private RadGridView gridView;

        /// <summary>Dataset holding Artefact and codelists tables</summary>
        private DataSet ds;

        /// <summary> In memory artefacts + loader </summary>
        private SmManager sm;

        /// <summary> Control for selecting DSD operational mode </summary>
        private ComboBox comboDsdMode;

        /// <summary> Control for codelist annotations selection </summary>
        private ucAnnotationSel annotationSelector;

        /// <summary> Control for Table prefix </summary>
        private RadTextBox  textBoxPrefix;

        #endregion

        #region Properties

        /// <summary> Indicates if grid allows filtering</summary>
        public bool EnableFilter 
        {
            get { return gridView.EnableFiltering; }

            set { gridView.EnableFiltering = value; }
        }

        #endregion

        #region Constructor

        public GridManager(RadGridView radGrid, ComboBox comboMode, RadTextBox  rtbPrefix, ucAnnotationSel annotSel)
        {
            textBoxPrefix = rtbPrefix;
            gridView = radGrid;
            annotationSelector = annotSel;
            sm = ReferenceManager.GetSmContainerImage();
            ds = ConstructDataSet();

            InitView(ds);

            comboDsdMode = comboMode;
            comboDsdMode.SelectedValueChanged += new EventHandler(comboDsdMode_SelectedValueChanged);


            // Event for defining size columns of grid combo multicell
          //  gridView.CellEditorInitialized += new GridViewCellEventHandler(gridView_CellEditorInitialized);
        }

        void comboDsdMode_SelectedValueChanged(object sender, EventArgs e)
        {
            if (gridView.CurrentRow != null && gridView.CurrentRow.Cells[ColType].Value.ToString() == TYPE_DSD)
            {
                gridView.CurrentRow.Cells[ColMode].Value = comboDsdMode.SelectedItem.ToString();
            }
        }

        #endregion

        #region Grid Columns construction

        public void InitView(DataSet ds)
        {

            gridView.BeginUpdate();

            gridView.AutoGenerateHierarchy = true;
            gridView.DataSource = ds;
            gridView.DataMember = TableMain;
            gridView.Relations[0].ChildTemplate.EnableAlternatingRowColor = true;
            gridView.Relations[0].ChildTemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
            gridView.Relations[0].ChildTemplate.Columns[ColGuid].IsVisible = false;
            gridView.Relations[0].ChildTemplate.Columns[ColType].ReadOnly = true;
            gridView.Relations[0].ChildTemplate.Columns[ColFullId].ReadOnly = true;
            gridView.Relations[0].ChildTemplate.AllowAddNewRow = false;
            gridView.MasterTemplate.Columns[ColGuid].IsVisible = false;
            gridView.MasterTemplate.Columns[ColType].ReadOnly = true;
            gridView.MasterTemplate.Columns[ColFullId].ReadOnly = true;
            gridView.MasterTemplate.Columns[ColStarParam].IsVisible = false;

            gridView.EndUpdate();

            gridView.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;

            gridView.RowsChanging += new GridViewCollectionChangingEventHandler(gridView_RowsChanging);
            gridView.RowsChanged += new GridViewCollectionChangedEventHandler(gridView_RowsChanged);
            gridView.SelectionChanging += new CancelEventHandler(gridView_SelectionChanging);
            gridView.SelectionChanged += new EventHandler(gridView_SelectionChanged);
            //           gridView.AddNewRowPosition = SystemRowPosition.Top;


        }

        void gridView_SelectionChanging(object sender, CancelEventArgs e)
        {
            // Current row is DSD, memorize mode
            if (gridView.CurrentRow != null && gridView.CurrentRow.Cells[ColType].Value.ToString() == TYPE_DSD)
            {
                int index = 0;
                foreach (string s in comboDsdMode.Items)
                {
                    if (s == gridView.CurrentRow.Cells[ColMode].Value.ToString())
                    {
                        comboDsdMode.SelectedIndex = index;
                        break;
                    }
                    ++index;

                }
            }

        }

        public void UpdateSelectedAnnotations(string selection)
        {
            if (gridView.CurrentRow != null && gridView.CurrentRow is GridViewDataRowInfo
                && RowHasAnnotationSupport(gridView.CurrentRow))
            {
                gridView.CurrentRow.Cells[ColMode].Value = selection;
            }
        }

        private CodeListType GetCodelist(GridViewRowInfo row)
        {
            CodeListType clt = null;
            if (row != null && row.Cells[ColType].Value.ToString() == TYPE_CODELIST)
            {
                ArtefactRefTyped art = new ArtefactRefTyped(SdmxArtefactType.CodeLists,
                   row.Cells[ColFullId].Value.ToString());
                clt = sm.GetArtefact(art.Type, art, true) as CodeListType;
            }
            return clt;
        }

        private ConceptSchemeType GetConceptScheme(GridViewRowInfo row)
        {
            ConceptSchemeType cst = null;
            if (row != null && row.Cells[ColType].Value.ToString() == TYPE_CONCEPTSCHEME)
            {
                ArtefactRefTyped art = new ArtefactRefTyped(SdmxArtefactType.Concepts,
                   row.Cells[ColFullId].Value.ToString());
                cst = sm.GetArtefact(art.Type, art, true) as ConceptSchemeType;
            }
            return cst;
        }

        private KeyFamilyType GetDsd(GridViewRowInfo row)
        {
            KeyFamilyType dsd = null;
            if (row != null && row.Cells[ColType].Value.ToString() == "DSD")
            {
                ArtefactRefTyped art = new ArtefactRefTyped(SdmxArtefactType.KeyFamilies,
                   row.Cells[ColFullId].Value.ToString());
                dsd = sm.GetArtefact(art.Type, art, true) as KeyFamilyType;
            }
            return dsd;
        }

        private DsdExportMode GetExportMode(GridViewRowInfo row)
        {
            if (row != null && row.Cells[ColType].Value.ToString() == "DSD")
            {
                string mode = row.Cells[ColMode].Value.ToString();
                for (int i = 0; i < comboDsdMode.Items.Count; i++)
                {
                    if (mode == comboDsdMode.Items[i].ToString())
                        return (DsdExportMode)i;
                }
            }
            return DsdExportMode.Linear;
        }

        
 

        private AnnotationInventory GetAnnotationInventory(GridViewRowInfo row)
        {
            AnnotationInventory anInventory = null;
            FlatHierarchicalList fhl = null;
            if (RowHasAnnotationSupport(row))
            {
                if (row.Cells[ColType].Value.ToString() == TYPE_CODELIST)
                {
                    // Fetch codelist
                    CodeListType clt = GetCodelist(row);
                    if (clt != null)
                        fhl = clt.CodeList;
                }
                else if (row.Cells[ColType].Value.ToString() == TYPE_CONCEPTSCHEME)
                {
                    ConceptSchemeType cst = GetConceptScheme(row);
                    if (cst != null)
                        fhl = cst.ConceptList;
                }

                if (fhl != null)
                {
                    anInventory = new AnnotationInventory();
                    foreach (ISdmxHierarchyListItem item in fhl)
                        anInventory.Inventory(item.AnnotationList, false);
                    DataTable tb = anInventory.GenDataTableInfo();

                    // Mark currently selected
                    string[] arr = row.Cells[ColMode].Value.ToString().Split(';');
                    foreach (string s in arr)
                    {
                        foreach (DataRow dr in tb.Rows)
                        {
                            if (dr["Ann"].ToString() == s)
                            {
                                dr["Sel"] = true;
                                break;
                            }
                        }
                    }
                }
            }
            return anInventory;
        }

        private bool RowHasAnnotationSupport(GridViewRowInfo row)
        {
            return (row != null && (row.Cells[ColType].Value.ToString() == TYPE_CODELIST || 
                row.Cells[ColType].Value.ToString() == TYPE_CONCEPTSCHEME));

        }
        void gridView_SelectionChanged(object sender, EventArgs e)
        {
            // Current row is Codelist or conceptscheme, display annotations selection
            if (gridView.CurrentRow != null && RowHasAnnotationSupport(gridView.CurrentRow))
            {
                // Get slist and mark selected
                AnnotationInventory anInventory = GetAnnotationInventory(gridView.CurrentRow);
                // Display
                annotationSelector.SetInventoryDataSource(anInventory);
            }
        }

        void gridView_RowsChanging(object sender, GridViewCollectionChangingEventArgs e)
        {
        }

        void gridView_RowsChanged(object sender, GridViewCollectionChangedEventArgs e)
        {
        }


        #endregion

        #region DB Code generation

        public StringBuilder GenerateDbCode(bool isOracleTarget)
        {
            StringBuilder sb = new StringBuilder();
            sm = ReferenceManager.GetSmContainerImage();

            if (gridView.CurrentRow != null && gridView.CurrentRow is GridViewDataRowInfo)
            {
                if (gridView.CurrentRow.Cells[ColType].Value.ToString() == TYPE_CODELIST)
                {
                    AnnotationInventory anInventory = GetAnnotationInventory(gridView.CurrentRow);
                    // Fetch codelist
                    CodeListType clt = GetCodelist(gridView.CurrentRow);
                    if (clt != null)
                    {
                        CodelistTable tb = new CodelistTable(clt, null, anInventory,
                            gridView.CurrentRow.Cells[ColTableName].Value.ToString(), textBoxPrefix.Text);
                        TableGenerator gen = isOracleTarget ? (TableGenerator)new OracleTableGenerator(tb, null) : (TableGenerator)new SasTableGenerator(tb);
                        sb = gen.GenerateTable();
                    }
                }
                else if (gridView.CurrentRow.Cells[ColType].Value.ToString() == TYPE_CONCEPTSCHEME)
                {
                    AnnotationInventory anInventory = GetAnnotationInventory(gridView.CurrentRow);
                    // Fetch concept
                    ConceptSchemeType cst = GetConceptScheme(gridView.CurrentRow);
                    if (cst != null)
                    {
                        ConceptSchemeTable tb = new ConceptSchemeTable(cst, anInventory,
                            gridView.CurrentRow.Cells[ColTableName].Value.ToString(), textBoxPrefix.Text);
                        TableGenerator gen = isOracleTarget ? (TableGenerator)new OracleTableGenerator(tb, null) : (TableGenerator)new SasTableGenerator(tb);
                        sb = gen.GenerateTable();
                    }
                }

                else if (gridView.CurrentRow.Cells[ColType].Value.ToString() == TYPE_DSD)
                {
                    gridView.EndEdit();

                    // Get DSD, get table prefix to use (optional)
                    KeyFamilyType dsd = GetDsd(gridView.CurrentRow);
                    string prefixTableMode = gridView.CurrentRow.Cells[ColPrefix].Value != null ?
                        gridView.CurrentRow.Cells[ColPrefix].Value.ToString() : string.Empty;

                    DataStructureDefTable tb = new DataStructureDefTable(dsd, sm, GetExportMode(gridView.CurrentRow), null,
                         gridView.CurrentRow.Cells[ColTableName].Value.ToString(), prefixTableMode);
                    
                    // Prepare list of options according to grid info
                    List<DimOptions> proposedList = new List<DimOptions>();
                    foreach (GridViewRowInfo childRow in gridView.CurrentRow.ChildRows)
                    {
                        proposedList.Add (new DimOptions(
                            childRow.Cells[ColFullId].Value.ToString(), 
                            childRow.Cells[ColTableName].Value.ToString(),
                            GetAnnotationInventory(childRow)));
                   }
                    // Run star schema generator
                    StarSchemeOraGenerator dimfact = new StarSchemeOraGenerator(tb, proposedList, GetDsdParameters(gridView.CurrentRow));
                    sb = dimfact.GenerateTable();
                }
            }
            return sb;
        }


        #endregion

        #region Data binding

        private DataSet ConstructDataSet()
        {
            DataSet ds = new DataSet();

            DataTable tMain = new DataTable(TableMain);
            tMain.Columns.Add(new DataColumn(ColGuid, typeof(string)));
            tMain.Columns.Add(new DataColumn(ColType, typeof(string)));
            tMain.Columns.Add(new DataColumn(ColTableName, typeof(string)));
            tMain.Columns.Add(new DataColumn(ColPrefix, typeof(string)));
            tMain.Columns.Add(new DataColumn(ColMode, typeof(string)));
            tMain.Columns.Add(new DataColumn(ColFullId, typeof(string)));
            tMain.Columns.Add(new DataColumn(ColStarParam, typeof(string)));

            DataTable tCodeList = new DataTable(TableRefCodelist);

            tCodeList.Columns.Add(new DataColumn(ColGuid, typeof(string)));
            tCodeList.Columns.Add(new DataColumn(ColType, typeof(string)));
            tCodeList.Columns.Add(new DataColumn(ColTableName, typeof(string)));
            tCodeList.Columns.Add(new DataColumn(ColMode, typeof(string)));
            tCodeList.Columns.Add(new DataColumn(ColFullId, typeof(string)));

            tMain.PrimaryKey = new DataColumn[] { tMain.Columns[ColGuid] };

            ds.Tables.Add(tMain);
            ds.Tables.Add(tCodeList);

            DataRelation dr = new DataRelation(CodelistRelation, tMain.Columns[ColGuid], tCodeList.Columns[ColGuid]);
            ds.Relations.Add(dr);
            return ds;
        }

        public void AddMainArtefact(Artefact artefact, DsdSasExportMode mode, string prefix)
        {
            DataTable tMain = ds.Tables[TableMain];
            DataTable tCodelist = ds.Tables[TableRefCodelist];

            using (new SdmxMl.Helper.CWaitCursor())
            {
                // On main level can add one of the following
                if (artefact is CodeListType)
                {
                    // Single main codelist artefact
                    CodeListType clt = artefact as CodeListType;
                    tMain.Rows.Add(new object[] {
                    Guid.NewGuid().ToString(),
                    SdmxArtefactType.CodeLists.ToString(),
                    CodelistArtefactTable.DefaultTableName(clt),
                    prefix,
                    string.Empty,
                    //UrnManager.FullIdToUrn(artefact.ArType, artefact.FullIdent).Remove(0, UrnManager.UrnBase.Length)
                    clt.FullIdent,
                    string.Empty
                });
                }
                else if (artefact is ConceptSchemeType)
                {
                    
                    // Single main conceptscheme artefact
                    ConceptSchemeType cScheme = artefact as ConceptSchemeType;
                    tMain.Rows.Add(new object[] {
                    Guid.NewGuid().ToString(),
                    SdmxArtefactType.Concepts.ToString(),
                    ConceptSchemeArtefactTable.DefaultTableName(cScheme),
                    prefix,
                    string.Empty,
                    cScheme.FullIdent,
                    string.Empty 
                });
                    
                }
                else if (artefact is KeyFamilyType)
                {
                    // Single main codelist artefact
                    KeyFamilyType dsd = artefact as KeyFamilyType;
                    string gid = Guid.NewGuid().ToString();
                    DataRow addedRow = tMain.Rows.Add(new object[] {
                    gid,
                    TYPE_DSD,
                    DsdTable.DefaultTableName(dsd),
                    prefix,
                    mode.ToString(),
                    //UrnManager.FullIdToUrn(artefact.ArType, artefact.FullIdent).Remove(0, UrnManager.UrnBase.Length)
                    dsd.FullIdent,
                    string.Empty
                });

                    // Now child codelists (linear mode obtains all codelists)
                    HashSet<string> loadedCodelists = new HashSet<string>();
                    DsdTable dsdTable = new DsdTable(dsd, sm, DsdSasExportMode.Linear, prefix);
                    HeaderColumns header = dsdTable.Header;
                    foreach (HeaderColumn col in header.Columns)
                    {
                        if (col.AttachedCodelist != null && col.AttachedCodelist.CltMain != null)
                        {
                            string fullId = col.AttachedCodelist.CltMain.FullIdent;
                            if (!loadedCodelists.Contains(fullId))
                            {
                                loadedCodelists.Add(fullId);
                                tCodelist.Rows.Add(new object[] {
                            gid,
                            SdmxArtefactType.CodeLists.ToString(),
                            CodelistArtefactTable.DefaultTableName(col.AttachedCodelist.CltMain),
                            string.Empty,
                            //UrnManager.FullIdToUrn(SdmxArtefactType.CodeLists, col.AttachedCodelist.CltMain.FullIdent).Remove(0, UrnManager.UrnBase.Length)
                            col.AttachedCodelist.CltMain.FullIdent
                            });
                            }
                        }
                    }
                    gridView.CurrentRow = gridView.MasterTemplate.Rows[gridView.MasterTemplate.Rows.Count - 1];
                }

            }
        }


        #endregion

        #region List persistence

        public void SaveList(string fileName)
        {
            ds.WriteXml(fileName, XmlWriteMode.WriteSchema);
        }

        public void LoadList(string fileName)
        {
            // Load as dataset
            DataSet dsLoad = new DataSet();
            dsLoad.ReadXml(fileName, XmlReadMode.ReadSchema);

            // check correct scheme
            if (dsLoad.Tables.Count != 2 || dsLoad.Tables[0].Columns.Count != ds.Tables[0].Columns.Count)
                throw new ApplicationException("Invalid artefact list: " + fileName);
            int index = 0;
            foreach (DataColumn dc in ds.Tables[0].Columns)
                if (ds.Tables[0].Columns[index++].ColumnName != dc.ColumnName)
                    throw new ApplicationException("Invalid column detected: " + dc.ColumnName);

            // replace current
            ds = dsLoad;
            InitView(ds);
        }
        #endregion

        #region Star Scheme Parameters

        private StarSchemeParameters GetDsdParameters(GridViewRowInfo row)
        {
            StarSchemeParameters schemeParameters = new StarSchemeParameters();
            if (row != null && row is GridViewDataRowInfo
               && row.Cells[ColType].Value.ToString() == TYPE_DSD)
            {
                if (row.Cells[ColStarParam].Value.ToString().Length > 0)
                {
                    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(schemeParameters.GetType());
                    schemeParameters = (StarSchemeParameters)serializer.Deserialize(new System.IO.StringReader(row.Cells[ColStarParam].Value.ToString()));
                }
            }
            return schemeParameters;
       }

        public void EditStarSchemeParameters()
        {
            if (gridView.CurrentRow != null && gridView.CurrentRow is GridViewDataRowInfo  
                && gridView.CurrentRow.Cells[ColType].Value.ToString() == TYPE_DSD)
            {
                // get dsd associated parameters
                StarSchemeParameters schemeParameters = GetDsdParameters(gridView.CurrentRow);
                
                // Raise parameters edit dialog
                DlgStarSchemeParam aDlg = new DlgStarSchemeParam(schemeParameters);
                if (aDlg.ShowDialog() == DialogResult.OK)
                {
                    // Save modified dsd parameters
                    System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(schemeParameters.GetType());
                    System.Text.StringBuilder sb = new System.Text.StringBuilder(128);
                    System.IO.StringWriter sw = new System.IO.StringWriter(sb);
                    x.Serialize(sw, schemeParameters);
                    gridView.CurrentRow.Cells[ColStarParam].Value = sb.ToString();

                }
            }

        }

        #endregion
    }
}
